/*!
    \copyright  Copyright (c) 2018 - 2025 Qualcomm Technologies International, Ltd.
                All Rights Reserved.
                Qualcomm Technologies International, Ltd. Confidential and Proprietary.
    \file
    \ingroup    volume_service
    \brief      Implementation of the Volume Service.
*/

#include "volume_service.h"

#include "audio_sources.h"
#include "audio_volume.h"
#include "volume_system.h"
#include "voice_sources.h"
#include "volume_messages.h"
#include "volume_mute.h"
#include "volume_utils.h"
#ifdef ENABLE_SYNCHRONISED_VOLUME
#include "volume_service_channel_event_spec.h"
#include "event_bus_subscribe.h"
#include "event_bus_publish.h"
#include "sync_event.h"
#endif

#include <av.h>
#include <focus_audio_source.h>
#include <focus_voice_source.h>
#include <focus_generic_source.h>
#include <panic.h>
#include <task_list.h>
#include <message.h>
#include <message_broker.h>
#include <logging.h>
#include <source_param_types.h>
#include <stdio.h>
#include <ui.h>

/* Make the type used for message IDs available in debug tools */
LOGGING_PRESERVE_MESSAGE_ENUM(volume_service_messages)
ASSERT_MESSAGE_GROUP_NOT_OVERFLOWED(VOLUME_SERVICE, VOLUME_SERVICE_MESSAGE_END)

#define VOLUME_SERVICE_CLIENT_TASK_LIST_INIT_CAPACITY 1

/*! Number of steps that volume should be moved in. This is a figure specific
    to User Interface initiated volume moves.

    If the configuration of the volume being moved requires fewer steps, that
    will be used. If that was not done, there is a risk that volume would not
    actually be changed.
 */
#define VOLUME_SERVICE_UI_STEPS     16

/*! Macro for creating messages */
#define MAKE_VOLUME_SERVICE_MESSAGE(TYPE) \
    TYPE##_T *message = PanicUnlessNew(TYPE##_T);

/*! \brief Internal message IDs */
enum
{
    INTERNAL_MSG_APPLY_AUDIO_VOLUME,
    INTERNAL_MSG_VOLUME_RAMP_REPEAT,
};

/*! Internal message for a volume repeat */
typedef struct
{
    generic_source_t source;    /*!< Source volume ramp is applied to */
    int16 step;                 /*!< Step to adjust volume by +ve or -ve */
} INTERNAL_MSG_VOLUME_RAMP_REPEAT_T;


/*! \brief Time between volume changes being applied for a volume ramp (in milliseconds). */
#define VOLUME_RAMP_REPEAT_TIME_MSECS               (300)

/* Ui Inputs in which volume service is interested*/
static const message_group_t ui_inputs[] =
{
    UI_INPUTS_VOLUME_MESSAGE_GROUP
};

static void volumeService_OnAudioRoutingChange(audio_source_t source, source_routing_change_t change);
static void volumeService_InternalMessageHandler( Task task, MessageId id, Message message );
static void volumeService_RefreshAudioVolume(event_origin_t origin, audio_source_t source,  volume_t new_volume);

static const audio_source_observer_interface_t volume_service_audio_observer_interface =
{
    .OnVolumeChange = NULL,
    .OnAudioRoutingChange = volumeService_OnAudioRoutingChange,
    .OnMuteChange = NULL,
};

typedef struct
{
    TASK_LIST_WITH_INITIAL_CAPACITY(VOLUME_SERVICE_CLIENT_TASK_LIST_INIT_CAPACITY)  client_list;
    TaskData volume_message_handler_task;
} volume_service_data;

static volume_service_data the_volume_service;

#define VolumeServiceGetClientLIst() (task_list_flexible_t *)(&the_volume_service.client_list)

static TaskData internal_message_task = { volumeService_InternalMessageHandler };

/*! \brief Make a volume change to a Voice Source.
*/
void VolumeService_ChangeVoiceSourceVolume(voice_source_t source, int16 step)
{
    int step_size = VolumeUtils_GetStepSize(VoiceSources_GetVolume(source).config);

    if(step == step_size)
    {
        Volume_SendVoiceSourceVolumeIncrementRequest(source, event_origin_local);
    }
    else if(step == -step_size)
    {
        Volume_SendVoiceSourceVolumeDecrementRequest(source, event_origin_local);
    }
    else
    {
        int new_volume = VolumeUtils_LimitVolumeToRange((VoiceSources_GetVolume(source).value + step),
                                                        VoiceSources_GetVolume(source).config.range);

        DEBUG_LOG("volumeService_ChangeVoiceVolume enum:voice_source_t:%d new=%d", source, new_volume);

        Volume_SendVoiceSourceVolumeUpdateRequest(source, event_origin_local, new_volume);
    }
}

/*! \brief Start a volume ramp for the specified Source

    Start a repeating volume change on the specified source.

    \param source  The generic source for which to start the volume ramp
    \param step    change to be applied to volume, +ve or -ve
*/
static void volumeService_StartVolumeRamp(generic_source_t source, int16 step)
{
    DEBUG_LOG_INFO("volumeService_StartVolumeRamp enum:source_type_t:%d step=%d", source.type, step);

    if (source.type == source_type_audio)
    {
        VolumeService_ChangeAudioSourceVolume(source.u.audio, step);
    }
    else if (source.type == source_type_voice)
    {
        VolumeService_ChangeVoiceSourceVolume(source.u.voice, step);
    }

    MAKE_VOLUME_SERVICE_MESSAGE(INTERNAL_MSG_VOLUME_RAMP_REPEAT);
    message->step = step;
    message->source = source;
    MessageSendLater(&internal_message_task, INTERNAL_MSG_VOLUME_RAMP_REPEAT, message,
                     VOLUME_RAMP_REPEAT_TIME_MSECS);
}

/*! \brief Stop a volume ramp

    Cancel any active repeating volume change on a source.
*/
static void volumeService_StopVolumeRamp(void)
{
    MessageCancelFirst(&internal_message_task, INTERNAL_MSG_VOLUME_RAMP_REPEAT);
}

void volumeService_OnAudioRoutingChange(audio_source_t source, source_routing_change_t change)
{
    UNUSED(source);

    if (change == source_unrouted)
    {
        volumeService_StopVolumeRamp();
    }
}

/* Apply a consistent number of UI steps if possible */
int VolumeService_GetUiStepSize(volume_config_t config)
{
    int volume_source_step_size = VolumeUtils_GetStepSize(config);

    int ui_step_size = (config.range.max - config.range.min + 1) / VOLUME_SERVICE_UI_STEPS;

    if (volume_source_step_size >= ui_step_size)
    {
        /* use the volume config step size. Volume may not change 
           if the value doe snot move by at least 1 volume step  */
        return volume_source_step_size;
    }
    DEBUG_LOG_INFO("VolumeService_GetUiStepSize, volume step of %d. calculated %d",
                    volume_source_step_size, ui_step_size);

    return ui_step_size;
}

/*! \brief Handles UI inputs passed to the Volume Service

    Invokes routines based on ui input received from ui module.

    \param[in] id - ui input

    \returns void
 */
static void volumeService_HandleUiInput(MessageId ui_input)
{
    generic_source_t source = {0};
    int step_size = 0;
    bool handle_ui_input = FALSE;

    DEBUG_LOG_FN_ENTRY("volumeService_HandleUiInput enum:ui_input_t:%d", ui_input);

    source = Focus_GetFocusedGenericSourceForAudioRouting();
    if (GenericSource_IsValid(source))
    {
        handle_ui_input = TRUE;
        if (GenericSource_IsAudio(source))
        {
            step_size = VolumeService_GetUiStepSize(AudioSources_GetVolume(source.u.audio).config);
        }
        else
        {
            step_size = VolumeService_GetUiStepSize(VoiceSources_GetVolume(source.u.voice).config);
        }
    }

    if (handle_ui_input)
    {
        switch (ui_input)
        {
        case ui_input_volume_down_start:
            step_size = -step_size;
            // Deliberate fall through
        case ui_input_volume_up_start:
            MessageCancelAll(&internal_message_task, ui_input);
            volumeService_StartVolumeRamp(source, step_size);
            break;

        case ui_input_volume_stop:
            volumeService_StopVolumeRamp();
            break;

        case ui_input_volume_down:
            step_size = -step_size;
            // Deliberate fall through
        case ui_input_volume_up:
            if (GenericSource_IsAudio(source))
            {
                VolumeService_ChangeAudioSourceVolume(source.u.audio, step_size);
            }
            else
            {
                VolumeService_ChangeVoiceSourceVolume(source.u.voice, step_size);
            }
            break;

        default:
            break;
        }
    }
}

void VolumeService_ChangeAudioSourceVolume(audio_source_t source, int16 step)
{
    int step_size = VolumeUtils_GetStepSize(AudioSources_GetVolume(source).config);

    DEBUG_LOG_FN_ENTRY("VolumeService_ChangeAudioSourceVolume");

    if(step == step_size)
    {
        Volume_SendAudioSourceVolumeIncrementRequest(source, event_origin_local);
    }
    else if(step == -step_size)
    {
        Volume_SendAudioSourceVolumeDecrementRequest(source, event_origin_local);
    }
    else
    {
        int new_volume = VolumeUtils_LimitVolumeToRange((AudioSources_GetVolume(source).value + step),
                                                         AudioSources_GetVolume(source).config.range);
        Volume_SendAudioSourceVolumeUpdateRequest(source, event_origin_local, new_volume);
    }
}

static bool volumeService_VolumeWithinAllowedRange(volume_t volume)
{
    return volume.value < volume.config.range.max && volume.value > volume.config.range.min;
}

static void volumeService_DoVolumeRampRepeat(MessageId id, INTERNAL_MSG_VOLUME_RAMP_REPEAT_T * msg)
{
    volume_t new_volume = { 0 };
    if (msg->source.type == source_type_audio)
    {
        audio_source_t source = msg->source.u.audio;
        volume_t volume = AudioSources_GetVolume(source);
        int volume_step_size = VolumeUtils_GetStepSize(volume.config);

        new_volume = volume;
        new_volume.value += msg->step;

        if (volumeService_VolumeWithinAllowedRange(volume))
        {
            if (msg->step == volume_step_size)
            {
                VolumeService_IncrementAudioSourceVolume(source, event_origin_local);
            }
            else if (msg->step == -volume_step_size)
            {
                VolumeService_DecrementAudioSourceVolume(source, event_origin_local);
            }
            else
            {
                VolumeService_SetAudioSourceVolume(source, event_origin_local, new_volume);
            }
        }
    }
    else if (msg->source.type == source_type_voice)
    {
        voice_source_t source = msg->source.u.voice;
        volume_t volume = VoiceSources_GetVolume(source);
        int volume_step_size = VolumeUtils_GetStepSize(volume.config);

        new_volume = volume;
        new_volume.value += msg->step;

        if (volumeService_VolumeWithinAllowedRange(volume))
        {
            if (msg->step == volume_step_size)
            {
                VolumeService_IncrementVoiceSourceVolume(source, event_origin_local);
            }
            else if (msg->step == -volume_step_size)
            {
                VolumeService_DecrementVoiceSourceVolume(source, event_origin_local);
            }
            else
            {
                VolumeService_SetVoiceSourceVolume(source, event_origin_local, new_volume);
            }
        }
    }
    else
    {
        Panic();
    }

    if (volumeService_VolumeWithinAllowedRange(new_volume))
    {
        MAKE_VOLUME_SERVICE_MESSAGE(INTERNAL_MSG_VOLUME_RAMP_REPEAT);
        memcpy(message, msg, sizeof(INTERNAL_MSG_VOLUME_RAMP_REPEAT_T));
        MessageSendLater(&internal_message_task, id, message, VOLUME_RAMP_REPEAT_TIME_MSECS);
    }
}

static void volumeService_InternalMessageHandler( Task task, MessageId id, Message msg )
{
    UNUSED(task);

    if (isMessageUiInput(id))
    {
        volumeService_HandleUiInput(id);
    }
    else
    {
        switch(id)
        {
        case INTERNAL_MSG_APPLY_AUDIO_VOLUME:
            {
                generic_source_t focused_source = Focus_GetFocusedGenericSourceForAudioRouting();
                if (GenericSource_IsAudio(focused_source))
                {
                    volume_t source_volume = AudioSources_GetVolume(focused_source.u.audio);
                    volumeService_RefreshAudioVolume(event_origin_local, focused_source.u.audio, source_volume);
                }
            }
            break;

        case INTERNAL_MSG_VOLUME_RAMP_REPEAT:
            volumeService_DoVolumeRampRepeat(id, (INTERNAL_MSG_VOLUME_RAMP_REPEAT_T *) msg);
            break;

        default:
            Panic();
            break;
        }
    }
}

#define volumeService_VolumeIsMax(volume) (volume.value >= volume.config.range.max)
#define volumeService_VolumeIsMin(volume) (volume.value <= volume.config.range.min)

static void volumeService_NotifyMinOrMaxVolume(volume_t volume)
{
    if(volumeService_VolumeIsMax(volume))
    {
        TaskList_MessageSendId(TaskList_GetFlexibleBaseTaskList(VolumeServiceGetClientLIst()), VOLUME_SERVICE_MAX_VOLUME);
    }
    if(volumeService_VolumeIsMin(volume))
    {
        TaskList_MessageSendId(TaskList_GetFlexibleBaseTaskList(VolumeServiceGetClientLIst()), VOLUME_SERVICE_MIN_VOLUME);
    }
}

static void volumeService_NotifyMinOrMaxGenericVolume(generic_source_t source, volume_t volume)
{
    if(volumeService_VolumeIsMax(volume) || volumeService_VolumeIsMin(volume))
    {
        generic_source_t routed_source = Focus_GetFocusedGenericSourceForAudioRouting();
        bool is_nothing_routed = !GenericSource_IsValid(routed_source);

        DEBUG_LOG_VERBOSE("volumeService_NotifyMinOrMaxGenericVolume src(enum:source_type_t:%d,%d), routed_src(enum:source_type_t:%d,%d)",
                          source.type, source.u.audio, routed_source.type, routed_source.u.audio);

        if((GenericSource_IsValid(routed_source) && GenericSource_IsSame(source, routed_source)) || is_nothing_routed)
        {
            volumeService_NotifyMinOrMaxVolume(volume);
        }
    }
}

static void volumeService_NotifyMinOrMaxAudioVolume(audio_source_t source, volume_t volume)
{
    GENERIC_AUDIO_SOURCE_MAKE(source);
    volumeService_NotifyMinOrMaxGenericVolume(generic_source, volume);
}

static void volumeService_NotifyMinOrMaxVoiceVolume(voice_source_t source, volume_t volume)
{
    GENERIC_VOICE_SOURCE_MAKE(source);
    volumeService_NotifyMinOrMaxGenericVolume(generic_source, volume);
}

static void volumeService_RefreshVoiceVolume(voice_source_t voice_source, volume_t new_volume)
{
    DEBUG_LOG_FN_ENTRY("volumeService_RefreshVoiceVolume enum:voice_source_t:%d", voice_source);
#ifdef ENABLE_TWM_STEREO
     volume_t volume = VoiceSources_CalculateOutputVolumeForNewVolume(voice_source, new_volume);
#else
    UNUSED(new_volume);
    volume_t volume = VoiceSources_CalculateOutputVolume(voice_source);
#endif /* ENABLE_TWM_STEREO */
    volume_parameters_t volume_params =
    {
        .source = {.type = source_type_voice, .u.voice = voice_source},
        .volume = volume
    };
    AudioVolume_Set(&volume_params);
}

#ifdef ENABLE_SYNCHRONISED_VOLUME

static inline bool volumeService_SyncEventWasGeneratedOnThisDevice(bool generated_on_primary)
{
    return (generated_on_primary == BtDevice_IsMyAddressPrimary());
}

static const event_bus_event_t volume_service_events[] = { volume_service_volume_update };

static void volumeService_HandleSyncedActionEvent(event_bus_channel_t channel, event_bus_event_t event, void * data, uint16 data_size, const event_bus_subscriber_t* subscriber)
{
    UNUSED(subscriber);
    PanicFalse(channel == EVENT_BUS_CHANNEL_VOLUME_SERVICE);
    switch(event)
    {
        case volume_service_volume_update:
            {
                volume_service_volume_update_t * volume_update_data = (volume_service_volume_update_t *)data;
                event_origin_t origin = volume_update_data->origin;
                if(volume_update_data->origin == event_origin_local && !volumeService_SyncEventWasGeneratedOnThisDevice(volume_update_data->was_generated_on_primary))
                {
                    origin = event_origin_peer;
                }
                DEBUG_LOG("volumeService_HandleSyncedActionEvent volume_service_volume_update origin=%d new_origin=%d", volume_update_data->origin, origin);
                if(volume_update_data->source.type == source_type_audio)
                {
                    VolumeService_SetAudioSourceVolume(volume_update_data->source.u.audio, origin, volume_update_data->volume);
                }
                else
                {
                    VolumeService_SetVoiceSourceVolume(volume_update_data->source.u.voice, origin, volume_update_data->volume);
                }
            }
            break;
        default:
            DEBUG_LOG_WARN("volumeService_HandleSyncedActionEvent unhandled event %d", event);
            break;
    }
}

static event_bus_subscriber_t volume_service_event_subscriber = { volumeService_HandleSyncedActionEvent, NULL };

#endif

static void volumeService_RefreshAudioVolume(event_origin_t origin, audio_source_t audio_source,  volume_t new_volume)
{
    UNUSED(origin);
#ifdef ENABLE_TWM_STEREO
    volume_t volume = AudioSources_CalculateOutputVolumeForNewVolume(audio_source, new_volume);
#else
    UNUSED(new_volume);
    volume_t volume = AudioSources_CalculateOutputVolume(audio_source);
#endif
    volume_parameters_t volume_params =
    {
        .source = {.type = source_type_audio, .u.audio = audio_source},
        .volume = volume
    };
    AudioVolume_Set(&volume_params);
}

static void volumeService_RefreshCurrentVolume(event_origin_t origin)
{
    generic_source_t focused_source = Focus_GetFocusedGenericSourceForAudioRouting();
    volume_t source_volume;
    DEBUG_LOG_INFO("volumeService_RefreshCurrentVolume src=(enum:source_type_t:%d,%d)",
                   focused_source.type,
                   focused_source.u.voice);

    if (GenericSource_IsVoice(focused_source))
    {
        source_volume = VoiceSources_GetVolume(focused_source.u.voice);
        volumeService_RefreshVoiceVolume(focused_source.u.voice, source_volume);
    }
    else if (GenericSource_IsAudio(focused_source))
    {
        source_volume = AudioSources_GetVolume(focused_source.u.audio);
        volumeService_RefreshAudioVolume(origin, focused_source.u.audio, source_volume);
    }
}

static void volumeService_UpdateAudioSourceVolume(audio_source_t source, volume_t new_volume, event_origin_t origin)
{
    DEBUG_LOG_FN_ENTRY("volumeService_UpdateAudioSourceVolume source = %d new_volume = %d origin = %d",source, new_volume.value, origin);

    bool is_lea_src = AudioSource_IsLeAudio(source)
    ;

    /* When remote sets the volume to zero (0), it also updates the mute state to MUTE, if volume changes to non-zero(>0) value (from zero)
       in remote then it changes the mute state to UNMUTE.When earbud changes the volume from zero to non-zero value then 
       it shall update the mute state to UNMUTE as it is done by the remote.*/
    if(is_lea_src && origin != event_origin_external && AudioSources_GetMuteState(source) == mute && new_volume.value != 0)
    {
        DEBUG_LOG_INFO("volumeService_UpdateAudioSourceVolume unmute");
        VolumeService_AudioSourceMute(source, origin, FALSE);
    }

    AudioSources_SetVolume(source, new_volume);
    AudioSources_OnVolumeChange(source, origin, new_volume);

    generic_source_t focused_source = Focus_GetFocusedGenericSourceForAudioRouting();

    /* Update volume for BREDR audio source if it focussed one and
       update volume for LEA audio source even if its not focussed and no BREDR audio source routed.
       We do this as there is one server for LEA volume */

    if ((GenericSource_IsAudio(focused_source) && focused_source.u.audio == source) ||
        (!AudioSource_IsA2dp(focused_source.u.audio) && !VoiceSource_IsHfp(focused_source.u.voice) && is_lea_src))
    {
        volumeService_RefreshAudioVolume(origin, source, new_volume);
    }
}

static void volumeService_UpdateSystemVolume(volume_t new_volume, event_origin_t origin)
{
    Volume_SetSystemVolume(new_volume);
    volumeService_RefreshCurrentVolume(origin);
}

static void volumeService_UpdateVoiceSourceLocalVolume(voice_source_t source, volume_t new_volume, event_origin_t origin)
{
    DEBUG_LOG_FN_ENTRY("volumeService_UpdateVoiceSourceLocalVolume source = %d new_volume = %d origin = %d",source, new_volume.value, origin);

    bool is_lea_src = VoiceSource_IsLeUnicastSource(source)
    ;

    /* When remote sets the volume to zero (0), it also updates the mute state to MUTE, if volume changes to non-zero(>0) value (from zero)
       in remote then it changes the mute state to UNMUTE.When earbud changes the volume from zero to non-zero value then 
       it shall update the mute state to UNMUTE as it is done by the remote.*/
    if(is_lea_src && origin != event_origin_external && VoiceSources_GetMuteState(source) == mute && new_volume.value != 0)
    {
        DEBUG_LOG_INFO("volumeService_UpdateVoiceSourceLocalVolume unmute");
        VolumeService_VoiceSourceMute(source, origin, FALSE);
    }

    VoiceSources_SetVolume(source, new_volume);

    generic_source_t focused_source = Focus_GetFocusedGenericSourceForAudioRouting();

    /* Update volume for BREDR voice source if it focussed one and
       update volume for LEA voice source even if its not focussed and no BREDR voice source routed.
       We do this as there is one server for LEA volume */

    if ((GenericSource_IsVoice(focused_source) && focused_source.u.voice == source) ||
        (!VoiceSource_IsHfp(focused_source.u.voice) && !AudioSource_IsA2dp(focused_source.u.audio) && is_lea_src))
    {
        VoiceSources_OnVolumeChange(source, origin, new_volume);
        volumeService_RefreshVoiceVolume(source, new_volume);
    }
}

void VolumeService_SetAudioSourceVolume(audio_source_t source, event_origin_t origin, volume_t new_volume)
{
    volume_t source_volume = AudioSources_GetVolume(source);
    DEBUG_LOG("VolumeService_SetAudioSourceVolume, enum:audio_source_t:%d enum:event_origin_t:%d volume %u",
              source, origin, new_volume.value);
    source_volume.value = VolumeUtils_ConvertToVolumeConfig(new_volume, source_volume.config);

    if(AudioSources_IsVolumeControlRegistered(source) && (origin == event_origin_local))
    {
        AudioSources_VolumeSetAbsolute(source, source_volume);
    }
    else
    {
        volumeService_UpdateAudioSourceVolume(source, source_volume, origin);
    }
    volumeService_NotifyMinOrMaxAudioVolume(source, source_volume);
}

void VolumeService_IncrementAudioSourceVolume(audio_source_t source, event_origin_t origin)
{
    DEBUG_LOG("VolumeService_IncrementAudioSourceVolume enum:audio_source_t:%d enum:event_origin_t:%d",
              source, origin);
    if (AudioSources_IsVolumeControlRegistered(source) && (origin == event_origin_local))
    {
        AudioSources_VolumeUp(source);
    }
    else
    {
        volume_t source_volume = AudioSources_GetVolume(source);
        source_volume.value = VolumeUtils_IncrementVolume(source_volume);
        volumeService_UpdateAudioSourceVolume(source, source_volume, origin);
        volumeService_NotifyMinOrMaxAudioVolume(source, source_volume);
    }
}

void VolumeService_DecrementAudioSourceVolume(audio_source_t source, event_origin_t origin)
{
    DEBUG_LOG("VolumeService_DecrementAudioSourceVolume enum:audio_source_t:%d enum:event_origin_t:%d",
              source, origin);
    if (AudioSources_IsVolumeControlRegistered(source) && (origin == event_origin_local))
    {
        AudioSources_VolumeDown(source);
    }
    else
    {
        volume_t source_volume = AudioSources_GetVolume(source);
        source_volume.value = VolumeUtils_DecrementVolume(source_volume);
        volumeService_UpdateAudioSourceVolume(source, source_volume, origin);
        volumeService_NotifyMinOrMaxAudioVolume(source, source_volume);
    }
}

void VolumeService_AudioSourceMute(audio_source_t source, event_origin_t origin, bool mute_state)
{
    DEBUG_LOG("VolumeService_AudioSourceMute enum:audio_source_t:%d enum:event_origin_t:%d mute_state %d",
                  source, origin, mute_state);
    AudioSources_SetMuteState(source, mute_state);
    AudioSources_OnMuteChange(source, origin, mute_state);
    volumeService_RefreshCurrentVolume(event_origin_local);
}

void VolumeService_SetSystemVolume(event_origin_t origin, volume_t new_volume)
{
    volume_t system_volume = Volume_GetSystemVolume();
    system_volume.value = VolumeUtils_ConvertToVolumeConfig(new_volume, system_volume.config);
    volumeService_UpdateSystemVolume(system_volume, origin);
}

void VolumeService_IncrementSystemVolume(event_origin_t origin)
{
    volume_t system_volume = Volume_GetSystemVolume();
    system_volume.value = VolumeUtils_IncrementVolume(system_volume);
    volumeService_UpdateSystemVolume(system_volume, origin);
}

void VolumeService_DecrementSystemVolume(event_origin_t origin)
{
    volume_t system_volume = Volume_GetSystemVolume();
    system_volume.value = VolumeUtils_DecrementVolume(system_volume);
    volumeService_UpdateSystemVolume(system_volume, origin);
}

void VolumeService_SetVoiceSourceVolume(voice_source_t source, event_origin_t origin, volume_t new_volume)
{
    volume_t source_volume = VoiceSources_GetVolume(source);
    DEBUG_LOG("VolumeService_SetVoiceSourceVolume enum:voice_source_t:%d enum:event_origin_t:%d volume %u",
              source, origin, new_volume.value);
    source_volume.value = VolumeUtils_ConvertToVolumeConfig(new_volume, source_volume.config);

    if(VoiceSources_IsVolumeControlRegistered(source) && (origin == event_origin_local))
    {
        VoiceSources_VolumeSetAbsolute(source, source_volume);
    }
    else
    {
        volumeService_UpdateVoiceSourceLocalVolume(source, source_volume, origin);
    }
    volumeService_NotifyMinOrMaxVoiceVolume(source, source_volume);
}

void VolumeService_IncrementVoiceSourceVolume(voice_source_t source, event_origin_t origin)
{
    DEBUG_LOG("VolumeService_IncrementVoiceSourceVolume enum:voice_source_t:%d enum:event_origin_t:%d",
              source, origin);
    if (VoiceSources_IsVolumeControlRegistered(source) && (origin == event_origin_local))
    {
        VoiceSources_VolumeUp(source);
    }
    else
    {
        volume_t source_volume = VoiceSources_GetVolume(source);
        source_volume.value = VolumeUtils_IncrementVolume(source_volume);
        volumeService_UpdateVoiceSourceLocalVolume(source, source_volume, origin);
        volumeService_NotifyMinOrMaxVoiceVolume(source, source_volume);
    }
}

void VolumeService_DecrementVoiceSourceVolume(voice_source_t source, event_origin_t origin)
{
    DEBUG_LOG("VolumeService_DecrementVoiceSourceVolume enum:voice_source_t:%d enum:event_origin_t:%d",
              source, origin);
    if (VoiceSources_IsVolumeControlRegistered(source) && (origin == event_origin_local))
    {
        VoiceSources_VolumeDown(source);
    }
    else
    {
        volume_t source_volume = VoiceSources_GetVolume(source);
        source_volume.value = VolumeUtils_DecrementVolume(source_volume);
        volumeService_UpdateVoiceSourceLocalVolume(source, source_volume, origin);
        volumeService_NotifyMinOrMaxVoiceVolume(source, source_volume);
    }
}

void VolumeService_VoiceSourceMute(voice_source_t source, event_origin_t origin, bool mute_state)
{
    DEBUG_LOG("VolumeService_VoiceSourceMute enum:voice_source_t:%d enum:event_origin_t:%d mute_state %d",
                      source, origin, mute_state);
    VoiceSources_SetMuteState(source, mute_state);
    VoiceSources_OnMuteChange(source, origin, mute_state);
    volumeService_RefreshCurrentVolume(event_origin_local);
}

static void volumeMessageHandler(Task task, MessageId id, Message message)
{
    UNUSED(task);

    switch(id)
    {
        case VOICE_SOURCE_VOLUME_UPDATE_REQUEST:
            {
                voice_source_volume_update_request_message_t *msg = (voice_source_volume_update_request_message_t *)message;
#ifdef ENABLE_SYNCHRONISED_VOLUME
                DEBUG_LOG("volumeMessageHandler VOICE_SOURCE_VOLUME_UPDATE_REQUEST enum:event_origin_t:%d", msg->origin);
                volume_service_volume_update_t volume_update = 
                { 
                    .was_generated_on_primary = BtDevice_IsMyAddressPrimary(), 
                    .source = { .type = source_type_voice, .u.audio = msg->voice_source },
                    .volume = msg->volume,
                    .origin = msg->origin,
                };
                EventBus_Publish(EVENT_BUS_CHANNEL_VOLUME_SERVICE, volume_service_volume_update, &volume_update, sizeof(volume_update));
#else
                VolumeService_SetVoiceSourceVolume(msg->voice_source, msg->origin, msg->volume);
#endif
            }
            break;
        case VOICE_SOURCE_VOLUME_INCREMENT_REQUEST:
            {
                voice_source_volume_increment_request_message_t *msg = (voice_source_volume_increment_request_message_t *)message;
#ifdef ENABLE_SYNCHRONISED_VOLUME
                DEBUG_LOG("volumeMessageHandler VOICE_SOURCE_VOLUME_UPDATE_REQUEST enum:event_origin_t:%d", msg->origin);
                volume_t current_volume = VoiceSources_GetVolume(msg->voice_source);
                volume_service_volume_update_t volume_update = 
                { 
                    .was_generated_on_primary = BtDevice_IsMyAddressPrimary(), 
                    .source = { .type = source_type_voice, .u.audio = msg->voice_source },
                    .volume = { .config = current_volume.config, .value = VolumeUtils_IncrementVolume(current_volume) },
                    .origin = msg->origin,
                };
                EventBus_Publish(EVENT_BUS_CHANNEL_VOLUME_SERVICE, volume_service_volume_update, &volume_update, sizeof(volume_update));
#else
                VolumeService_IncrementVoiceSourceVolume(msg->voice_source, msg->origin);
#endif
            }
            break;
        case VOICE_SOURCE_VOLUME_DECREMENT_REQUEST:
            {
                voice_source_volume_decrement_request_message_t *msg = (voice_source_volume_decrement_request_message_t *)message;
#ifdef ENABLE_SYNCHRONISED_VOLUME
                DEBUG_LOG("volumeMessageHandler VOICE_SOURCE_VOLUME_UPDATE_REQUEST enum:event_origin_t:%d", msg->origin);
                volume_t current_volume = VoiceSources_GetVolume(msg->voice_source);
                volume_service_volume_update_t volume_update = 
                { 
                    .was_generated_on_primary = BtDevice_IsMyAddressPrimary(), 
                    .source = { .type = source_type_voice, .u.audio = msg->voice_source },
                    .volume = { .config = current_volume.config, .value = VolumeUtils_DecrementVolume(current_volume) },
                    .origin = msg->origin,
                };
                EventBus_Publish(EVENT_BUS_CHANNEL_VOLUME_SERVICE, volume_service_volume_update, &volume_update, sizeof(volume_update));
#else
                VolumeService_DecrementVoiceSourceVolume(msg->voice_source, msg->origin);
#endif
            }
            break;
        case VOICE_SOURCE_MUTE_VOLUME_REQUEST:
            {
                voice_source_mute_volume_request_message_t *msg = (voice_source_mute_volume_request_message_t *)message;
                VolumeService_VoiceSourceMute(msg->voice_source, msg->origin, msg->mute_state);
            }
            break;
        case AUDIO_SOURCE_VOLUME_UPDATE_REQUEST:
            {
                audio_source_volume_update_request_message_t *msg = (audio_source_volume_update_request_message_t *)message;
#ifdef ENABLE_SYNCHRONISED_VOLUME
                DEBUG_LOG("volumeMessageHandler AUDIO_SOURCE_VOLUME_UPDATE_REQUEST enum:event_origin_t:%d", msg->origin);
                volume_service_volume_update_t volume_update = 
                { 
                    .was_generated_on_primary = BtDevice_IsMyAddressPrimary(), 
                    .source = { .type = source_type_audio, .u.audio = msg->audio_source }, 
                    .volume = msg->volume,
                    .origin = msg->origin,
                };
                EventBus_Publish(EVENT_BUS_CHANNEL_VOLUME_SERVICE, volume_service_volume_update, &volume_update, sizeof(volume_update));
#else
                VolumeService_SetAudioSourceVolume(msg->audio_source, msg->origin, msg->volume);
#endif
            }
            break;
        case AUDIO_SOURCE_VOLUME_INCREMENT_REQUEST:
            {
                audio_source_volume_increment_request_message_t *msg = (audio_source_volume_increment_request_message_t *)message;
#ifdef ENABLE_SYNCHRONISED_VOLUME
                DEBUG_LOG("volumeMessageHandler AUDIO_SOURCE_VOLUME_UPDATE_REQUEST enum:event_origin_t:%d", msg->origin);
                volume_t current_volume = AudioSources_GetVolume(msg->audio_source);
                volume_service_volume_update_t volume_update = 
                { 
                    .was_generated_on_primary = BtDevice_IsMyAddressPrimary(), 
                    .source = { .type = source_type_audio, .u.audio = msg->audio_source }, 
                    .volume = { .config = current_volume.config, .value = VolumeUtils_IncrementVolume(current_volume) },
                    .origin = msg->origin,
                };
                EventBus_Publish(EVENT_BUS_CHANNEL_VOLUME_SERVICE, volume_service_volume_update, &volume_update, sizeof(volume_update));
#else
                VolumeService_IncrementAudioSourceVolume(msg->audio_source, msg->origin);
#endif
            }
            break;
        case AUDIO_SOURCE_VOLUME_DECREMENT_REQUEST:
            {
                audio_source_volume_decrement_request_message_t *msg = (audio_source_volume_decrement_request_message_t *)message;
#ifdef ENABLE_SYNCHRONISED_VOLUME
                DEBUG_LOG("volumeMessageHandler AUDIO_SOURCE_VOLUME_UPDATE_REQUEST enum:event_origin_t:%d", msg->origin);
                volume_t current_volume = AudioSources_GetVolume(msg->audio_source);
                volume_service_volume_update_t volume_update = 
                { 
                    .was_generated_on_primary = BtDevice_IsMyAddressPrimary(), 
                    .source = { .type = source_type_audio, .u.audio = msg->audio_source }, 
                    .volume = { .config = current_volume.config, .value = VolumeUtils_DecrementVolume(current_volume) },
                    .origin = msg->origin,
                };
                EventBus_Publish(EVENT_BUS_CHANNEL_VOLUME_SERVICE, volume_service_volume_update, &volume_update, sizeof(volume_update));
#else
                VolumeService_DecrementAudioSourceVolume(msg->audio_source, msg->origin);
#endif
            }
            break;
        case AUDIO_SOURCE_MUTE_VOLUME_REQUEST:
            {
                audio_source_mute_volume_request_message_t *msg = (audio_source_mute_volume_request_message_t *)message;
                VolumeService_AudioSourceMute(msg->audio_source, msg->origin, msg->mute_state);
            }
            break;
        default:
            break;
    }
}

bool VolumeService_Init(Task init_task)
{
    UNUSED(init_task);
    TaskList_InitialiseWithCapacity(VolumeServiceGetClientLIst(), VOLUME_SERVICE_CLIENT_TASK_LIST_INIT_CAPACITY);

    the_volume_service.volume_message_handler_task.handler = volumeMessageHandler;
    Volume_RegisterForMessages(&the_volume_service.volume_message_handler_task);

    Ui_RegisterUiInputConsumer(&internal_message_task, ui_inputs, ARRAY_DIM(ui_inputs));

    /* Register for all audio sources, to obtain indications when an audio source becomes unrouted,
       in order to cancel any currently active volume ramp operations. */
    audio_source_t source = audio_source_none;
    while(++source < max_audio_sources)
    {
        AudioSources_RegisterObserver(source, &volume_service_audio_observer_interface);
    }

    return TRUE;
}

bool VolumeService_SyncActionInit(Task init_task)
{
    UNUSED(init_task);
#ifdef ENABLE_SYNCHRONISED_VOLUME
    SyncEvent_SubscribeSyncEventsArray(EVENT_BUS_CHANNEL_VOLUME_SERVICE, volume_service_events, volume_service_event_subscriber);
#endif
    return TRUE;
}

static void volumeService_RegisterMessageGroup(Task task, message_group_t group)
{
    PanicFalse(group == VOLUME_SERVICE_MESSAGE_GROUP);
    TaskList_AddTask(TaskList_GetFlexibleBaseTaskList(VolumeServiceGetClientLIst()), task);
}

MESSAGE_BROKER_GROUP_REGISTRATION_MAKE(VOLUME_SERVICE, volumeService_RegisterMessageGroup, NULL);
